package com.wanji.gateway.device;

import cn.hutool.core.thread.ThreadFactoryBuilder;
import com.wanji.gateway.command.DeviceCommand;
import io.netty.buffer.ByteBuf;
import io.netty.channel.Channel;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelFutureListener;
import jakarta.annotation.PreDestroy;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

import java.util.Map;
import java.util.concurrent.*;

@Service
@Slf4j
@RequiredArgsConstructor
public class CommandScheduler {
    private final Map<String, PriorityBlockingQueue<DeviceCommand>> commandQueues
            = new ConcurrentHashMap<>();
    private final Map<String, ScheduledFuture<?>> retryTasks = new ConcurrentHashMap<>();
    // 使用自定义线程池配置
    private final ScheduledExecutorService executor = new ScheduledThreadPoolExecutor(
            Runtime.getRuntime().availableProcessors() * 2,
            new ThreadFactoryBuilder()
                    .setDaemon(true)
                    .build(),
            new ThreadPoolExecutor.CallerRunsPolicy()
    );

    public void scheduleInitialization(String deviceId) {
        if (deviceId == null || deviceId.isEmpty()) {
            log.error("无法调度空设备ID的初始化任务");
            return;
        }
        commandQueues.computeIfAbsent(deviceId, id ->
                new PriorityBlockingQueue<>(100, (cmd1, cmd2) ->
                        Integer.compare(cmd2.getPriority().getValue(), cmd1.getPriority().getValue())));
    }

    public void submitCommand(String deviceId, DeviceCommand command) {
        if (deviceId == null || command == null) {
            return;
        }
        commandQueues.computeIfAbsent(deviceId, id ->
                        new PriorityBlockingQueue<>(100, (cmd1, cmd2) ->
                                Integer.compare(cmd2.getPriority().getValue(), cmd1.getPriority().getValue())
                        ))
                .offer(command);
        processNextCommand(deviceId);
    }

    private void processNextCommand(String deviceId) {
        DeviceCommand command = commandQueues.get(deviceId).poll();
        if (command != null) {
            sendCommand(deviceId, command);
        }
    }

    private void sendCommand(String deviceId, DeviceCommand command) {
        try {
            Channel channel = DeviceManager.getChannel(deviceId);
            if (channel != null && channel.isActive() && channel.isWritable()) {
                byte[] data = command.encode();
                ByteBuf buf = channel.alloc().buffer(data.length);
                buf.writeBytes(data);
                ChannelFuture future = channel.writeAndFlush(buf);
                future.addListener((ChannelFutureListener) f -> {
                    if (!f.isSuccess()) {
                        handleSendFailure(deviceId, command, (Exception) f.cause());
                    }
                });
                // startAckTimer(deviceId, command);
            }
        } catch (Exception e) {
            handleSendFailure(deviceId, command, e);
        }
    }

    private void handleSendFailure(String deviceId, DeviceCommand command, Exception e) {
        // 可记录日志或重试机制
        log.error("Failed to send command to device: {}", deviceId, e);
    }

    private void startAckTimer(String deviceId, DeviceCommand command) {
        String commandId = command.getId();
        if (commandId == null) {
            commandId = "fallback-" + System.currentTimeMillis();
        }

        ScheduledFuture<?> future = executor.schedule(() -> {
            if (!command.isAcknowledged()) {
                handleAckTimeout(deviceId, command);
            }
        }, command.getTimeout(), TimeUnit.MILLISECONDS);

        retryTasks.put(commandId, future);
    }

    private void handleAckTimeout(String deviceId, DeviceCommand command) {
        // 清理定时任务
        ScheduledFuture<?> future = retryTasks.remove(command.getId());
        if (future != null) {
            future.cancel(false);
        }

        // 可尝试重发或记录日志
    }

    @PreDestroy
    public void destroy() {
        executor.shutdownNow();
        retryTasks.forEach((id, task) -> task.cancel(false));
        retryTasks.clear();
    }
}
